XTuner Domino EP
结论
截至 XTuner commit 9989fb7,相关技术可以按下面四层理解:[^xtuner-commit]
| 层次 | 技术 | 解决的问题 | 是否能与 Domino EP 组合 |
|---|---|---|---|
| 并行语义 | 普通 EP | 把不同 experts 放到不同 rank,路由 token 后再合并 | Domino EP 的基础 |
| 通信后端 | All-to-All / DeepEP / AGRS | 具体怎样 dispatch 和 combine token | 可以,三选一作为底层 dispatcher |
| 调度方法 | Domino EP | 怎样用多个 micro-batch 隐藏 EP 通信 | 本身不替代 dispatcher |
| 融合算子 | MC2 | 在一个 micro-batch 内融合通信与 GMM/MM | 思想上正交,但 XTuner 当前未接入该 dispatcher |
因此,最准确的比较不是“Domino EP 和 DeepEP 谁替代谁”,而是:
- All-to-All、DeepEP、AGRS 比通信实现。
- Domino EP 比跨微批调度。
- MC2 比单微批内的算子融合。
执行路径
XTuner 的入口参数是 intra_layer_micro_batch。训练引擎不再逐个 micro-batch 调用模型,而是按该参数切成列表;相应的梯度累积次数也会除以这个数。[^train-group]
1 | for i in range(0, len(data_batches), intra_layer_micro_batch): |
进入每个 MoE decoder layer 后,_micro_batch_forward 分成三个阶段:[^moe-schedule]
- 批量准备:各微批依次完成 attention、router 和异步
dispatch_preprocess。 - 分发与专家计算:为各微批发起 dispatch,只在消费结果前建立必要的事件依赖,然后执行 experts 和
combine_preprocess。 - 批量回收:发起各微批的 combine,最后逐个
combine_postprocess,再做残差和 shared experts 合并。
下面是从实际实现压缩出的关键控制流,省略了上下文保存代码:
1 | for hidden_states, seq_ctx, position_embeddings in micro_batches: |
关键不在 Python 循环本身,而在 dispatcher 维护的通信 stream、计算 stream 和 CUDA event。CPU 可以继续提交后续微批的工作;GPU 则在真正使用数据的位置等待事件,从而形成“微批 A 算 experts,微批 B 做 dispatch/combine”的流水。
这也解释了它为什么叫 Domino:一个微批的通信被下一个微批的计算接力遮住。但它仍然不是完整的 pipeline parallel 调度,也不是原论文实现的直接复制。
通信后端
XTuner 的 build_dispatcher 在 ep_size > 1 时支持 deepep、all2all 和 agrs;未指定时默认选择 all2all。[^dispatcher-selection] Domino EP 对这三种后端调用同一组 preprocess/dispatch/combine 接口。
All-to-All
普通 All-to-All 路径先按 expert 排列 token,交换各 rank 的 token 计数,计算可变长 input_splits 和 output_splits,再调用 autograd 版本的 all_to_all_single。[^all2all]
优点是语义直接、依赖少、最适合作为正确性基线;缺点是负载不均时通信尺寸不规则,而且 token count、重排和两次 All-to-All 的成本更容易暴露在关键路径上。
这里的“普通 EP”通常就是“EP 语义 + 普通 All-to-All dispatcher + intra_layer_micro_batch=1”,而不是另一种与 All-to-All 无关的算法。
DeepEP
DeepEP 是为 MoE dispatch/combine 定制的通信库。XTuner 使用 low-latency buffer,先计算 dispatch layout,再以 async_finish=True 发起 dispatch,并通过 event handle 表达依赖;实现还把 DeepEP 通信占用的 SM 数设为 20。[^deepep-op]
它的优势是对 NVLink/RDMA 拓扑、跨节点转发和低延迟 kernel 做了专门优化;代价是依赖自定义 CUDA 通信栈,对版本、拓扑和 buffer 配置更敏感。需要特别注意:XTuner 的构建脚本固定的是 DeepEP **v1.2.1 commit 9af0e0d**,不能把 DeepEP 当前主分支或 V2 的公开 benchmark 直接当成 XTuner 实测结果。[^deepep-pin]
DeepEP 负责让一次通信更快,Domino EP 负责尽量把这次通信藏起来。 二者通常是互补关系。
AGRS
AGRS 在 XTuner 中可按 AllGather + ReduceScatter 理解:dispatch 时 AllGather hidden states,同时用小规模 All-to-All 交换 top-k IDs/weights;本地 experts 计算后,combine 用 ReduceScatter(sum) 把结果送回 token 所属 rank。[^agrs-code]
它用更规则的 collective 替换可变长 token All-to-All,在某些拓扑和 collective 实现上更容易获得稳定吞吐。不过当前实现有严格前提:必须使用 grouped router,并要求 ep_size == router_n_groups == 8。[^agrs-constraint]
此外,AGRS 会 AllGather activation,通信量和在途 buffer 不一定更小;ReduceScatter 的求和顺序也与普通路径不同。XTuner 的对照测试使用 atol=rtol=1e-2,对应 PR 也明确提示了浮点归约顺序造成的小误差。[^agrs-test]
与 MC2 对比
此前 MindSpeed-MM 接入的 EP MC2 面向 Ascend NPU,把 AllToAllv -> GroupedMatmul 和 GroupedMatmul -> AllToAllv 分别融合为 NPU fused operator。[^mc2-pr] 它让算子内部把大通信和大计算切块、流水,减少中间张量、launch 和单微批内的串行等待。
| 维度 | Domino EP | EP MC2 |
|---|---|---|
| 重叠粒度 | 跨多个 micro-batch | 一个 micro-batch 的 fused operator 内部 |
| 核心机制 | 异步 stream/event 调度 | 通信与 GMM/MM 融合、切块流水 |
| 最低并发来源 | 至少两个可同时在途的微批 | 一个微批也可产生算子内流水 |
| 通信后端 | 可搭配 All-to-All、DeepEP、AGRS | 绑定支持 MC2 的 NPU/HCCL 算子 |
| 可移植性 | 调度思想较通用,但实现仍依赖 dispatcher | 强硬件、CANN 与算子版本约束 |
| 主要代价 | 更高激活峰值、事件调度与小 GEMM | 融合算子约束、调试困难、潜在精度差异 |
两者在理论上可以叠加:用 MC2 优化每个微批的 dispatch/GMM,再用 Domino 在微批之间继续遮蔽剩余气泡。但 XTuner 当前没有 MC2 dispatcher,而且两种机制都会争用通信带宽、计算单元和 stream;真正组合需要新接入与 profiler 证明,不能默认收益相加。MindSpeed-LLM 的 MC2 文档也明确提示部分模型可能出现精度问题,并列出了模型与硬件限制。[^mc2-doc]
论文证据
原始 Domino 论文处理的是 tensor parallel:沿 batch 维切输入、沿输出维切权重,让一个切片的 AllReduce 与另一个切片的矩阵乘重叠。论文报告在 DGX-H100 集群上相对 Megatron-LM 最高约 1.3× 加速。[^domino-paper] 这为“切分独立工作并用异步通信跨切片重叠”提供了机制证据,但通信对象是 TP AllReduce,不是 MoE EP 的 token dispatch/combine。
与 XTuner 实现更接近的论文先例来自 DeepSeek-V3:其预填充阶段同时处理两个 micro-batch,使一个微批的 attention/MoE 计算与另一个微批的 dispatch/combine 重叠。[^deepseek-v3] 不过 DeepSeek-V3 的 DualPipe 还包含 pipeline parallel 调度,不能与 XTuner 的单个 MoE 层实现画等号。
截至上述 XTuner commit,公开代码和 PR 能证明实现、测试与持续修复过程,但没有提供可复用的 Domino EP 专项性能表。PR #1078 是功能接入,后续 PR #1824 修复了 H2D 同步延迟第二个微批 DeepEP dispatch 的问题;这恰好说明收益依赖细致的异步路径,而不能只看开关名称。[^xtuner-prs]
选择方法
建议把“后端”和“调度”拆开做 A/B,而不是一次同时更改:
- 建立基线:
dispatcher="all2all"、intra_layer_micro_batch=1,确认 loss、吞吐、峰值显存和通信占比。 - 只换后端:分别测 DeepEP 或满足约束时的 AGRS,保持 global batch、micro-batch shape、EP size 和精度配置不变。
- 再开 Domino:先把
intra_layer_micro_batch改为 2,确认数据批次数可整除,并保留相同 global batch 语义。 - 看时间线而非开关:确认 dispatch/combine 确实与另一微批的 GMM 重叠,而不是被 H2D copy、host sync 或 event wait 串行化。
- 同时验精度:比较前向、梯度、loss 曲线和长程收敛;AGRS、低精度 DeepEP 与 MC2 都不能只用 step time 验收。
一个最小配置思路如下,具体对象名应以所用 XTuner recipe 为准:
1 | model_cfg.dispatcher = "deepep" # 也可单独测试 all2all / agrs |
总结
XTuner Domino EP 的本质是:把原本串行的梯度累积微批提升为 MoE 层内部可共同调度的工作集合,用通信 stream 和 event 建立跨微批流水。 它不改变 EP 的专家切分,也不规定 token 必须通过哪一种 collective 搬运。
- 追求通用基线与易调试,优先 All-to-All。
- NVIDIA 多机 MoE 且拓扑合适,优先评估 DeepEP + Domino EP。
- 满足
EP=router groups=8且 AG/RS collective 更占优,可评估 AGRS + Domino EP。 - Ascend 路径若已有成熟 fused operator,MC2 更像单微批内的深度融合;它与 Domino 的比较是算子融合对跨微批调度,不是同一层替代关系。
最终选择应由同一模型、同一 batch 语义下的 profiler、显存和收敛结果决定,而不是由技术名称决定。
[^xtuner-commit]: XTuner commit 9989fb74130d8f848662c5021458480ba161b9fa。本文源码结论均固定到该版本,避免主分支变化造成歧义。
[^train-group]: XTuner train_engine.py:微批分组与梯度累积。
[^moe-schedule]: XTuner moe_decoder_layer.py:跨微批 MoE 调度。
[^dispatcher-selection]: XTuner build_dispatcher:三种 dispatcher 与默认 All-to-All。
[^all2all]: XTuner torch_all2all.py:token count、split 与 All-to-All。
[^deepep-op]: XTuner deepep_op.py:DeepEP SM 配置;low-latency buffer 与异步 dispatch。
[^deepep-pin]: XTuner image_build.sh:DeepEP v1.2.1 固定版本;DeepEP 官方仓库。
[^agrs-code]: XTuner agrs.py:AllGather、All-to-All IDs/weights 与 ReduceScatter。
[^agrs-constraint]: XTuner MoEConfig:AGRS grouped router 与 8-way 约束。
[^agrs-test]: XTuner AGRS/All-to-All 对照测试;XTuner PR #1245。
[^mc2-pr]: MindSpeed-MM PR #2480:feat: support ep mc2。
[^mc2-doc]: MindSpeed-LLM:MC2 特性说明。
[^domino-paper]: Domino: Eliminating Communication in LLM Training via Generic Tensor Slicing and Overlapping。
[^deepseek-v3]: DeepSeek-V3 Technical Report。
[^xtuner-prs]: XTuner PR #1078:Domino EP;XTuner PR #1824:DeepEP dispatch overlap 修复。